package rs.etf.kdp.workstation;

import java.awt.Color;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JOptionPane;
import javax.swing.JTextArea;

import rs.ac.bg.etf.sleep.simulation.Event;
import rs.etf.kdp.server.EventFromWS;
import rs.etf.kdp.server.InterthreadBuffer;
import rs.etf.kdp.server.Part;

/**
 * @author Djordje Vukovic
 * @version 1.0
 * @created 25-May-2011 9:45:04 PM
 */
public class ConnectionToServer extends Thread{

	private Socket server;
	private String host;
	private int numberOfThreads;
	private int port;
	JTextArea out;
	ObjectInputStream input;
	ObjectOutputStream output;
	private String myAddress;
	private String name;
	List<SimulationThread> listOfSimulations;
	ExecutorService pool;
	
	public ConnectionToServer(String host, int port, int numberOfThreads, String name, JTextArea out){
		listOfSimulations = new ArrayList<SimulationThread>();
		this.name = name;
		this.port = port;
		this.host = host;
		this.out = out;
		this.numberOfThreads = numberOfThreads;
		try {
			myAddress = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			out.append("\nError while obtaining workstations IP address");
		}
		pool = Executors.newFixedThreadPool(numberOfThreads);
	}

	public void finalize() throws Throwable {
		if (server!=null)
		{
			try{
				server.close();
			} catch(Exception e)
			{
				out.append("\nError while closing socket in connection server");
			}
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void run()
	{
		try {
			server = new Socket(host, port);
		} catch (UnknownHostException e) {
			out.append("\nError: Unknown host");
			return;
		} catch (IOException e) {
			out.append("\nError while establishing connection");
			return;
		}

		try {
			output = new ObjectOutputStream(server.getOutputStream());
		} catch (IOException e) {
			out.append("\nError while creating output stream");
			return;
		}
		
		try {
			input = new ObjectInputStream(server.getInputStream());
		} catch (IOException e) {
			out.append("\nError while creating input stream");
		}
		
		String initialCommand = "workstation register " + numberOfThreads + " " + myAddress + " " + name;
		try {
			output.writeObject(initialCommand);
		} catch (IOException e) {
			out.append("\nError while sending register message to server");
			return;
		}
		
		String ack = null;
		try {
			ack = (String)input.readObject();
		} catch (IOException e) {
			out.append("\nError while receiving ack message from server");
			return;
		} catch (ClassNotFoundException e) {
			out.append("\nClassNotFound error while recieving ack message");
			return;
		}
		
		if(!ack.equals("workstation registrated")) 
		{
			if(ack.equals("invalid_name"))
			{
				JOptionPane.showMessageDialog(null, "Name that you entered is already in use. Choose different name and reconnect");
				WorkstationUI.lblWorkstationStatus.setText("Workstation is not running");
				WorkstationUI.lblWorkstationStatus.setForeground(Color.red);
				WorkstationUI.btnRunWorkstation.setEnabled(true);
				return;
			} else {
				out.append("\nError: Connection refused by server");
				return;
			}
		}
		
		while(true) //main loop
		{
			//out.append("\nEntered main loop");
			synchronized(this) {

				String command = null;
				try {
					command = (String)input.readObject();
				} catch (IOException e) {
					out.append("\nError while receiving first command in main loop");
				} catch (ClassNotFoundException e) {
					out.append("\nClassNotFound while receiving first command in main loop");
				}

				if (command==null) continue;
				//			if(command.equals("heartbeat"))
				//			{
				//				respondToHeartbeat(output, myAddress, out);
				//			} else 

				if(command.startsWith("part"))
				{
					String[] msgTok = command.split(" ");
					out.append("\nEntered part");
					String answer = "send_part " + msgTok[1];
					try {
						output.writeObject(answer);
						output.flush();
					} catch (IOException e) {
						out.append("\nUnable to send permission to send part of job");
						return;
					}
					out.append("\nNow receiving Part object");
					Part myPart = null;
					try {
						myPart = (Part)input.readObject();
					} catch (IOException e) {
						out.append("\nUnable to receive Part object");
						return;
					} catch (ClassNotFoundException e) {
						out.append("\nClassNotFound while receiving Part object");
						return;
					}
					InterthreadBuffer buff = new InterthreadBuffer();
					SimulationThread simT = new SimulationThread(myPart, out, output, buff);
					listOfSimulations.add(simT);
					pool.submit(simT);
				} else if(command.startsWith("abort"))
				{
					String[] tok = command.split(" ");
					long id = Long.parseLong(tok[1]);
					SimulationThread simThread = null;
					for(int i=0; i < listOfSimulations.size(); i++)
					{
						if(listOfSimulations.get(i).getMyPart().getServerId()==id)
						{
							simThread = listOfSimulations.get(i);
						}
					}
					if(simThread==null) continue;
					simThread.setEnd(true);	
					simThread.setAbort(true);
					simThread.interrupt();
				} else if(command.startsWith("request"))
				{
					String[] tok = command.split(" ");
					long id = Long.parseLong(tok[1]);
					SimulationThread simThread = null;
					for(int i=0; i < listOfSimulations.size(); i++)
					{
						if(listOfSimulations.get(i).getMyPart().getServerId()==id)
						{
							simThread = listOfSimulations.get(i);
						}
					}
					if(simThread==null) continue;
					simThread.setEnd(true);
					simThread.setAbort(false);
					simThread.interrupt();
				} else if(command.startsWith("transport"))
				{
					EventFromWS wsevent = null;
					try {
						wsevent = (EventFromWS)input.readObject();
					} catch (Exception e) {
						out.append("\nUnable to receive transport of event");
					} 

					String[] tok = command.split(" ");
					Long id = Long.parseLong(tok[1]);
					SimulationThread simT = null;
					int size = listOfSimulations.size();
					for(int i=0 ; i<size; i++)
					{
						if(listOfSimulations.get(i).getMyPart().getServerId()==id)
						{
							simT = listOfSimulations.get(i);
						}
					}

					PrioritySimBuffer buff= (PrioritySimBuffer)simT.getSimBuffer();
					buff.putInLocalBuffer((Event)wsevent.getEvent());
				} 

			}
		}
		
	}
	
//	private void respondToHeartbeat(ObjectOutputStream oos, String ip, JTextArea out)
//	{
//		String answer = "heartbeat_answer " + ip;
//		
//		try {
//			oos.writeObject(answer);
//		} catch (IOException e) {
//			System.out.println("Error while responding to heartbeat");
//		}
//		out.append("\nSuccessfully responded to heartbeat");
//	}
}